"use strict";
const nodemailer = require("nodemailer");
const Koa = require("koa");
const router = require("koa-router")();
const app = new Koa();
const UserModel = require("./models/UserModel");
const ExamAcCode = require("./models/ExamAcCode");
const EmailCode = require("./models/EmailCode");
const TokenModel = require("./models/TokenModel");
const QaModel = require("./models/QaModel");
const CaseModel = require("./models/CaseModel");
const cors = require("koa2-cors");
const bodyParser = require("koa-bodyparser");
var CryptoJS = require("crypto-js");
//upload
const { koaBody } = require("koa-body");
const koa_static = require("koa-static");
const fs = require("fs");
const path = require("path");
const checkVip = false;

app.use(koaBody({
  multipart: true,
  formidable: {
    maxFileSize: 200 * 1024 * 1024,
    keepExtension: true
  }
}));

function tokenValid(ctx) {

}

router.post("/activate", async ctx => {
  console.log('activate start', ctx.request.body)
  const uname = ctx.request.body.username;
  const code = ctx.request.body.code;
  var acCode = await ExamAcCode.findOne({ code: code });
  if (acCode === null) {
    console.log('code invalid')
    ctx.body = {
      code: 401,
      msg: "激活码无效"
    };
    return;
  }
  if (acCode.used) {
    console.log('code already used')
    ctx.body = {
      code: 401,
      msg: "该激活码已使用"
    };
    return;
  }
  var user = await UserModel.findOne({ username: uname });
  // console.log('user', user);
  var dayMills = 24 * 60 * 60 * 1000;
  const deadlineMills = new Date(user.deadline).getTime();
  const actMills = new Date(user.acTime).getTime();
  const hasActivated = !isNaN(actMills);
  console.log('hasActivated', hasActivated);

  var newData = {};
  var vipValid = false;
  if (hasActivated) {
    vipValid = Date.now() < deadlineMills;
  }
  if (vipValid) {
    user.deadline = deadlineMills + acCode.months * 30 * dayMills;
  } else {
    user.acTime = Date.now();
    user.deadline = actMills + acCode.months * 30 * dayMills;
  }
  var updatedUser = false
  await user.save((err, doc) => {
    if (err) {
      updatedUser = false;
      // 错误处理
      console.error('更新用户数据失败', err);
      return;
    }
    updatedUser = true;
    console.log('更新用户数据成功');
  })
  console.log('updatedUser', updatedUser)
  if (updatedUser) {
    // 更新或插入成功后的操作
    acCode.used = true
    const ret = await acCode.save((err, doc) => {
      if (err) {
        ctx.body = {
          code: 400,
          msg: "激活码激活状态更改失败" + err
        };
        console.error('激活码激活状态更改失败', err);
        return;
      }
      ctx.body = {
        code: 200,
        deadline: user.deadline,
        msg: "激活成功，增加天数：" + (30 * acCode.months)
      };
      console.log('更新激活码状态成功');
    })
  } else {
    console.error('更新用户数据失败');
    ctx.body = {
      code: 400,
      msg: "更新用户数据失败"
    };
  }
})

router.post("/getQas", async ctx => {
  console.log('Query qas...')
  const uname = ctx.request.body.username;
  const tk = ctx.request.body.token;
  const examIndex = ctx.request.body.examIndex;
  console.log("examIndex:", examIndex)
  var data = await TokenModel.findOne({ username: uname, token: tk });
  var dayMills = 24 * 60 * 60 * 1000;
  if (data != null && (Date.now() - parseInt(data.timeMills)) < 5 * dayMills) {
    console.log('Token valid')
    // check vip
    if (checkVip) {
      var user = await UserModel.findOne({ username: uname });
      if (isNaN(user.acTime) || Date.now() > parseInt(data.deadline)) {
        console.log('vip invalid')
        ctx.body = {
          code: 402,
          msg: "会员到期，请购买会员"
        };
        return
      }
    }
    var they = await QaModel.find({ examIndex: examIndex }).sort({ "order": 1 });
    ctx.body = {
      code: 200,
      res: they,
      msg: "GET qas success"
    };
  } else {
    console.log('Token invalid')
    ctx.body = {
      code: 401,
      msg: "登陆失效，请重新登陆"
    };
  }
})

router.post("/getCases", async ctx => {
  console.log('Query case...')
  const uname = ctx.request.body.username;
  const tk = ctx.request.body.token;
  const examIndex = ctx.request.body.examIndex;
  var data = await TokenModel.findOne({ username: uname, token: tk });
  var dayMills = 24 * 60 * 60 * 1000;
  if (data != null && (Date.now() - parseInt(data.timeMills)) < 5 * dayMills) {
    console.log('Token valid')
    // check vip
    if (checkVip) {
      var user = await UserModel.findOne({ username: uname });
      if (isNaN(user.acTime) || Date.now() > parseInt(data.deadline)) {
        console.log('vip invalid')
        ctx.body = {
          code: 402,
          msg: "会员到期，请购买会员"
        };
        return
      }
    }
    var they = await CaseModel.find({ examIndex: examIndex });
    ctx.body = {
      code: 200,
      res: they,
      msg: "GET case success"
    };
  } else {
    console.log('Token invalid')
    ctx.body = {
      code: 401,
      msg: "登陆失效，请重新登陆"
    };
  }
  console.log('Query case done')
})

router.post("/register", async ctx => {
  console.log(ctx.request.body);
  var uname = ctx.request.body.username;
  var psw = ctx.request.body.psw;
  var pswMD5 = CryptoJS.MD5(psw).toString();
  var data = await UserModel.find({ username: uname });
  ctx.set('Content-Type', 'application/json');
  if (data.length > 0) {
    ctx.body = JSON.stringify({
      code: 400,
      msg: '已存在的用户名,试试别的吧'
    });
  } else {
    var admin = uname === "admin";
    const person = await UserModel.create({
      username: uname,
      password: pswMD5,
      fullname: 'none',
      phone: 'none',
      email: 'none',
      admin: admin
    });
    // console.log(person);
    ctx.body = JSON.stringify({
      code: 200,
      msg: "注册成功"
    });
  }
});

const transporter = nodemailer.createTransport({
  service: "hotmail",
  auth: {
    user: 'hyc_exam@outlook.com',
    // pass: 'hyc_exa!1'
    pass: 'vkjasxtuodxxjkww'
  }
});

router.post("/getEmailCode", async ctx => {
  console.log('getEmailCode start', ctx.request.body);
  var uname = ctx.request.body.username;
  var data = await UserModel.findOne({ username: uname });
  ctx.set('Content-Type', 'application/json');
  if (data) {
    let code = '';
    for (let i = 0; i < 6; i++) {
      code += Math.floor(Math.random() * 10);
    }
    const ecode = await EmailCode.create({
      email: uname,
      code: code,
    });
    // console.log(ecode);
    try {
      const info = await transporter.sendMail({
        from: '"安全码" <hyc_exam@outlook.com>', // sender address
        to: uname, // list of receivers
        subject: "账号验证码", // Subject line
        text: "你的邮箱验证码", // plain text body
        html: "<b>" + ("你的验证码是：" + code + "，验证码三分钟内有效。\n如果你没有进行相关操作，请忽略此邮件") + "</b>", // html body
      });
      console.log("Message sent: %s", info.messageId);
      ctx.body = JSON.stringify({
        code: 200,
        msg: "发送验证码成功，请在邮箱查收"
      });
    } catch (err) {
      ctx.body = JSON.stringify({
        code: 400,
        msg: '发送验证码失败'
      });
      console.log("错误信息：" + err);
    }
  } else {
    ctx.body = JSON.stringify({
      code: 400,
      msg: '该用户不存在,请检查用户名'
    });
  }
});

router.post("/resetPsw", async ctx => {
  console.log(ctx.request.body);
  var uname = ctx.request.body.username;
  var psw = ctx.request.body.psw;
  var emailCode = ctx.request.body.emailCode;
  var pswMD5 = CryptoJS.MD5(psw).toString();
  var user = await UserModel.findOne({ username: uname });
  ctx.set('Content-Type', 'application/json');
  if (user) {
    let ecode = await EmailCode.findOne({ code: emailCode });
    let now = Date.now();
    let tsp = new Date(ecode.tsp).getTime();
    if (ecode && ((now - tsp) < 3 * 60 * 1000)) {
      user.password = pswMD5;
      await user.save()
        .then(result => {
          ctx.body = JSON.stringify({
            code: 200,
            msg: '重置密码成功，请登录'
          });
        })
        .catch(err => {
          console.error(err);
          ctx.body = JSON.stringify({
            code: 400,
            msg: '更改密码失败'
          });
        });
    } else {
      ctx.body = JSON.stringify({
        code: 400,
        msg: '无效的验证码'
      });
    }
    await EmailCode.deleteMany({ email: uname })
      .then((result) => {
        console.log(`Deleted EmailCode: ${result}`);
      })
      .catch(error => {
        console.error('delete emailcode fail', error);
      });
  } else {
    ctx.body = JSON.stringify({
      code: 400,
      msg: '该用户不存在,请检查用户名'
    });
  }
});

router.post("/login", async ctx => {
  console.log("login:" + ctx.request.body.username);
  var uname = ctx.request.body.username;
  var psw = ctx.request.body.psw;
  var pswMD5 = CryptoJS.MD5(psw).toString();
  var data = await UserModel.find({ username: uname, password: pswMD5 });
  if (data.length > 0) {
    const tm = Date.now();
    const tk = await TokenModel.create({
      username: uname,
      token: CryptoJS.MD5(uname + tm).toString(),
      timeMills: tm
    });
    ctx.body = {
      code: 200,
      token: tk.token,
      deadline: data[0].deadline,
      msg: "登陆成功"
    };
  } else {
    ctx.body = {
      code: 404,
      msg: "用户名或密码错误"
    };
  }
});

router.post("/checkToken", async ctx => {
  console.log("checkToken: ", ctx.request.body);
  var uname = ctx.request.body.username;
  var tk = ctx.request.body.token;
  var data = await TokenModel.findOne({ username: uname, token: tk });
  var user = await UserModel.findOne({ username: uname });
  var dayMills = 24 * 60 * 60 * 1000;
  // token 在创建后5天有效，三天后调此接口可以刷新token
  if (data != null && (Date.now() - parseInt(data.timeMills)) < 5 * dayMills) {
    console.log("checkToken valid ");
    var newToken = tk;
    if ((Date.now() - parseInt(data.timeMills)) > 3 * dayMills) {
      // await TokenModel.deleteOne({ username: uname, token: tk });
      const tm = Date.now();
      const newData = {
        username: uname,
        token: CryptoJS.MD5(uname + tm).toString(),
        timeMills: tm
      };
      TokenModel.findOneAndUpdate(
        { username: uname }, // 查询条件
        newData, // 更新的数据
        // 选项：upsert - 若文档不存在则创建之，new - 返回更新后的文档，runValidators - 运行验证器以应用模型定义的验证规则
        { upsert: true, new: true, runValidators: true },
        (err, doc) => {
          if (err) {
            // 错误处理
            console.error(err);
            return;
          }
          // 更新或插入成功后的操作
          console.log('User updated/inserted:', doc);
        }
      );
      newToken = newData.token
    }
    ctx.body = {
      code: 200,
      token: newToken,
      deadline: user.deadline,
      msg: "token有效"
    };
  } else {
    // await TokenModel.deleteOne({ username: uname, token: tk });
    ctx.body = {
      code: 400,
      msg: "登陆过期，请重新登陆"
    };
    console.log("checkToken invalid ");
  }
});

router.post("/uploadQA", async ctx => {
  console.log("uploadQA: ");
  QaModel.create(ctx.request.body,
    (err, doc) => {
      if (err) {
        // 错误处理
        console.error("uploadQA err: " + err);
        return;
      }
      // 更新或插入成功后的操作
      console.log('uploadQA success');
    }
  );
  ctx.body = {
    code: 200,
    msg: "提交成功"
  };
});

router.post("/uploadCases", async ctx => {
  console.log("uploadCases");
  CaseModel.create(ctx.request.body,
    (err, doc) => {
      if (err) {
        // 错误处理
        console.error("uploadCases err: " + err);
        return;
      }
      // 更新或插入成功后的操作
      console.log('uploadCases success');
    }
  );
  ctx.body = {
    code: 200,
    msg: "提交成功"
  };
});

router.post("/genAcCode", async ctx => {
  console.log(ctx.request.body);
  var uname = ctx.request.body.username;
  var psw = ctx.request.body.psw;
  var pswMD5 = CryptoJS.MD5(psw).toString();
  var months = ctx.request.body.months;
  var size = ctx.request.body.size;
  var data = await UserModel.find({ username: uname, password: pswMD5 });
  ctx.set('Content-Type', 'application/json');
  if (data.length > 0) {
    const user = data[0];
    const isAdmin = user.admin;
    if (isAdmin) {
      var codes = [];
      for (let i = 0; i < size; i++) {
        const raw = '' + i + user.password + Date.now();
        const acCode = CryptoJS.MD5(raw).toString();
        await ExamAcCode.create({
          code: acCode,
          months: months
        });
        codes.push(acCode);
      }
      ctx.body = JSON.stringify({
        code: 200,
        result: codes,
        msg: '创建成功'
      });
      return
    }
    ctx.body = JSON.stringify({
      code: 400,
      msg: '生成失败'
    });
  }
});

router.get("/", async ctx => {
  const path = require('path');
  const filePath = path.join(__dirname, '/login.html');
  ctx.response.type = 'html';
  ctx.body = fs.readFileSync(filePath);
});

// 全局中间件，用于处理 Authorization 验证
// app.use(async (ctx, next) => {
//   if (ctx.path === '/login'
//     || ctx.path === '/register'
//     || ctx.path === '/checkToken'
//     || ctx.path === '/uploadCases'
//     || ctx.path === '/uploadQA'
//     || ctx.path === '/getCases'
//     || ctx.path === '/getQas'
//     || ctx.path === '/'
//     || ctx.path.indexOf('.') > -1) {
//     await next();
//   } else {
//     // 验证用户是否有效
//     const uname = ctx.request.body.username;
//     const tk = ctx.request.body.token;
//     var data = await TokenModel.findOne({ username: uname, token: tk });
//     var dayMills = 24 * 60 * 60 * 1000;
//     if (data != null && (Date.now() - parseInt(data.timeMills)) < 5 * dayMills) {
//       console.log('Token valid')
//       await next();
//     } else {
//       // await TokenModel.deleteOne({ username: uname, token: tk });
//       ctx.body = {
//         code: 401,
//         msg: ctx.request.url + "登陆过期，请重新登陆"
//       };
//     }
//   }
// });
app.use(bodyParser());
app.use(cors());
// app.use(koa_static(`${process.cwd()}`));
app.use(koa_static(`${process.cwd()}/dist`));
app.use(router.routes());
app.listen(86);